#include "dialogprocessvideo.h"
#include "ui_dialogprocessvideo.h"


DialogProcessVideo::DialogProcessVideo(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogProcessVideo)
{
    ui->setupUi(this);
    setWindowTitle("Process The Video Data to Create 3D Data");


    runningLineProcessing=false;
    runningTriang3D=false;

    timer=new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(TimerEvent()));
    infoGood=false;  //set to false, until info is read
    looping=false; //set to not looping
    playing=true; //set to not playing
    timer->start(20);


}

DialogProcessVideo::~DialogProcessVideo()
{
    delete ui;

    //if lineprocessors are running, kill them  (we exited without pushing terminate, and they were still running)
    if(runningLineProcessing){
        emit termLinProc(); //send signal to our threads asking them to wrap-it-up
    }
}

void DialogProcessVideo::on_pushButton_LineProcessing_clicked()
{
    if(runningTriang3D) return;  //don't start if Triang3D is going. (not likely since button is disabled)

        if(!runningLineProcessing){  //if false it is a start request

            //check if previous results already exist
            if(QFile(output0).exists() || QFile(output1).exists())
                if(QMessageBox::question(this,
                               "Output Files Exist",
                               "Overwrite existing files?",
                               QMessageBox::Yes|QMessageBox::No,
                               QMessageBox::No   //default is "no"
                               ) != QMessageBox::Yes)
                    return;  // if "Yes" is selected then continue, otherwise break

            // Camera0 Video File
            setLineProcessingParams(lineProc0);

            // Camera0 Video File
            setLineProcessingParams(lineProc1);

            emit doItLinProc(); //signal to start execution in their thread

            ui->pushButton_LineProcessing->setText("Terminate");
            runningLineProcessing=true;

            playing=0; //shut video playback off if it is running

            ui->pushButton_Playback->setEnabled(false); //temporarily disable video play
            ui->pushButton_triang3D->setEnabled(false); //temporarily disable 3D triangulation
            ui->pushButton_TestParams->setEnabled(false); //temporarily disable running parameter test

        }
        else  // the push button event was a terminate request
            if(QMessageBox::question(this,
                           "Line Processing",
                           "Terminate the line processors?",
                           QMessageBox::Yes|QMessageBox::No,
                           QMessageBox::No   //default is "no"
                           ) == QMessageBox::Yes) {  // if "Yes" terminate, otherwise ignore
                emit termLinProc(); //send signal to our threads asking them to terminate
                lineProcessingDone(); //call for cleanup to restore our dialog buttons
            }
}

void DialogProcessVideo::updateProgressBar0(int progress) {
    ui->progressBar_cam0->setValue(progress);
}

void DialogProcessVideo::updateProgressBar1(int progress) {
    ui->progressBar_cam1->setValue(progress);
}

void DialogProcessVideo::updateLineImage0(){

    img0=lineProc0->qimgLineImage; //shallow copy - This may need to be converted to a deep copy
    emit updateBigPic(0,img0);
    int w = ui->label_Cam0_Video->width();
    ui->label_Cam0_Video->setPixmap(QPixmap::fromImage(lineProc0->qimgLineImage).scaledToWidth(w,Qt::FastTransformation));

}

void DialogProcessVideo::updateLineImage1(){
    img1=lineProc1->qimgLineImage; //shallow copy - This may need to be converted to a deep copy
    emit updateBigPic(1,img1);
    int w = ui->label_Cam1_Video->width();
    ui->label_Cam1_Video->setPixmap(QPixmap::fromImage(lineProc1->qimgLineImage).scaledToWidth(w,Qt::FastTransformation));
}



void DialogProcessVideo::on_pushButton_Playback_clicked()
{
    if(runningLineProcessing) return;  //don't play videos, line processing is sending images
    if(!playing){
        ui->pushButton_Playback->setText("Stop Playback");
        playing =!playing;
        if(infoGood){ //play the video files

            //calculate playback speed
            int averageFrameTime=timeVid/framesVid;  //milliseconds per frame
            timer->start(averageFrameTime);

        }
    }
    else {
        ui->pushButton_Playback->setText("Playback");
        playing =!playing;
        timer->stop();
    }

}


void DialogProcessVideo::TimerEvent(){

    if(!playing) { //if playing was turned off by lineProc or triangulate
        timer->stop();
        ui->pushButton_Playback->setText("Playback");  //turn the text back
        return;
    }
    if(!vid0.isOpened()) {
        vid0.open(videoname0.toStdString());
    }
    if(!vid1.isOpened()) {
        vid1.open(videoname1.toStdString());
    }

    if(vid0.isOpened()) {
        //check for end of fileint curpos=vid1.get(CV_CAP_PROP_POS_FRAMES);
        int curpos=vid0.get(CV_CAP_PROP_POS_FRAMES);
        int eof=vid0.get(CV_CAP_PROP_FRAME_COUNT);
        if(curpos<eof)  {
            vid0 >> matVid;
            cvtColor(matVid,matVid,CV_BGR2RGB);


            img0 =QImage((uchar*)matVid.data,matVid.cols,matVid.rows,
                           matVid.step,QImage::Format_RGB888).copy();

            int w = ui->label_Cam0_Video->width();

            ui->label_Cam0_Video->setPixmap(QPixmap::fromImage(img0).scaledToWidth(w,Qt::FastTransformation));

            emit updateBigPic(0,img0);
            ui->progressBar_cam0_playback->setValue((curpos+1)*100/eof);

        }
        else {

            if(looping) vid0.set(CV_CAP_PROP_POS_FRAMES,0); //rewind
            else {
                timer->stop();
                ui->pushButton_Playback->setText("Playback");
                playing=false;
                vid0.release();
            }
        }
    }
    if(vid1.isOpened()) {
        //check for end of file
        int curpos=vid1.get(CV_CAP_PROP_POS_FRAMES);
        int eof=vid1.get(CV_CAP_PROP_FRAME_COUNT);
        if(curpos<eof) {
            vid1 >> matVid;
            cvtColor(matVid,matVid,CV_BGR2RGB);

            img1=QImage((uchar*)matVid.data,matVid.cols,matVid.rows,
                           matVid.step,QImage::Format_RGB888).copy();

            int w = ui->label_Cam1_Video->width();  //fixed jitter !!!

            ui->label_Cam1_Video->setPixmap(QPixmap::fromImage(img1).scaledToWidth(w,Qt::FastTransformation));

            emit updateBigPic(1,img1);

            ui->progressBar_cam1_playback->setValue((curpos+1)*100/eof);

        }
        else {

            if(looping) vid1.set(CV_CAP_PROP_POS_FRAMES,0); //rewind
            else {
                timer->stop();
                ui->pushButton_Playback->setText("Playback");
                playing=false;
                vid1.release();
            }
        }
    }
}

void DialogProcessVideo::init() {

    //make directory if not done already
    dirResults.setPath(eventName + "/ProcessedResults");
    if (!dirResults.exists())
        dirResults.mkpath(".");

    //set the video file names
    videoname0 = eventName+"/VideoFiles/Cam0.avi";
    videoname1 = eventName+"/VideoFiles/Cam1.avi";

    //set the output files for line processing results
    output0= dirResults.path() + "/cam0_lineProcessingResult.yml.gz";
    output1= dirResults.path() + "/cam1_lineProcessingResult.yml.gz";

    //set the output files for 3D triangulation results
    output3D_0= dirResults.path() + "/cam0_3Dpts.yml.gz";
    output3D_1= dirResults.path() + "/cam1_3Dpts.yml.gz";


    //open the video Info file to get frames & time
    FileStorage fs;
    QString filename=eventName+"/VideoFiles/VideoInfo.yml";
    fs.open(filename.toStdString(),FileStorage::READ);
    if(fs.isOpened()) {
        fs["EventTime"]>>timeVid;  //milliseconds
        fs["FramesGrabbed"]>>framesVid;
        if(timeVid&&framesVid) infoGood=true;
    }
    int averageFrameTime=timeVid/framesVid;  //milliseconds per frame
    timer->start(averageFrameTime);

    //setup threads & objects inside them
    thread0 = new QThread;
    lineProc0 =  new lineProcessing(videoname0,output0);
    lineProc0->moveToThread(thread0);
    connect(this, SIGNAL(doItLinProc()), lineProc0, SLOT(doIt()),Qt::QueuedConnection);
    connect(this, SIGNAL(termLinProc()), lineProc0, SLOT(terminateProcessing()),Qt::DirectConnection); //using direct connection so the terminate gets done even while the doIt function is running.  Queued connection would just wait...
    connect(lineProc0, SIGNAL(status(int)), this, SLOT( updateProgressBar0(int)));
    connect(lineProc0, SIGNAL(lineImage()), this, SLOT( updateLineImage0()));
    connect(lineProc0, SIGNAL(finished()), this, SLOT( lineProcessingDone()));
    thread0->start();

    thread1 = new QThread;
    lineProc1 =  new lineProcessing(videoname1,output1);
    lineProc1->moveToThread(thread1);
    connect(this, SIGNAL(doItLinProc()), lineProc1, SLOT(doIt()),Qt::QueuedConnection);
    connect(this, SIGNAL(termLinProc()), lineProc1, SLOT(terminateProcessing()),Qt::DirectConnection); //using direct connection so the terminate gets done even while the doIt function is running.  Queued connection would just wait...
    connect(lineProc1, SIGNAL(status(int)), this, SLOT( updateProgressBar1(int)));
    connect(lineProc1, SIGNAL(lineImage()), this, SLOT( updateLineImage1()));
    connect(lineProc1, SIGNAL(finished()), this, SLOT( lineProcessingDone()));
    thread1->start();

    //add the triangulation objects to the threads
    triang3D_0 = new triangulate3D();
    triang3D_0->moveToThread(thread0);
    connect(this,SIGNAL(doItTriang3D()),triang3D_0,SLOT(doIt()));
    connect(this,SIGNAL(termTriang3D()),triang3D_0,SLOT(terminateProcessing()),Qt::DirectConnection);
    connect(triang3D_0,SIGNAL(status(int)),ui->progressBar_cam0_triang3D,SLOT(setValue(int)));
    connect(triang3D_0, SIGNAL(finished()), this, SLOT( triang3DProcessingDone()));

    triang3D_1 = new triangulate3D();
    triang3D_1->moveToThread(thread1);
    connect(this,SIGNAL(doItTriang3D()),triang3D_1,SLOT(doIt()));
    connect(this,SIGNAL(termTriang3D()), triang3D_1, SLOT(terminateProcessing()),Qt::DirectConnection);
    connect(triang3D_1,SIGNAL(status(int)),ui->progressBar_cam1_triang3D,SLOT(setValue(int)));
    connect(triang3D_1, SIGNAL(finished()), this, SLOT( triang3DProcessingDone()));

    //add FE export object to a thread
    fe = new FEexport(projName,eventName);
    fe->moveToThread(thread0);
    connect(this,SIGNAL(doItFEexport()),fe,SLOT(doIt()));
    connect(fe,SIGNAL(status(int)),ui->progressBar_FE_export,SLOT(setValue(int)));
    connect(fe,SIGNAL(done()),this,SLOT(FE_export_done()));

}

void DialogProcessVideo::on_checkBox_clicked()
{
    looping=!looping;
}


void DialogProcessVideo::on_pushButton_TestParams_clicked()
{
    if(runningLineProcessing) return;  //if we are already running the lineprocessing just ignore the button click

    playing=0; //shut off video playback (or it just overwrites the display)

    //first processor
    setLineProcessingParams(lineProc0);
    lineProc0->resultsOutput = false; //this tells the processor we only want to test.

    //second processor
    setLineProcessingParams(lineProc1);
    lineProc1->resultsOutput = false; //this tells the processor we only want to test.

    emit doItLinProc(); //signal to start execution in their thread

}

void DialogProcessVideo::setLineProcessingParams(lineProcessing *lineProc)
{
    lineProc->filterOnOff = ui->checkBox_Filter->isChecked();  // yes/no: use Gauss filter on line processing
    lineProc->minConfExport = ui->spinBox_MinConfForExport->value(); //minimum Confidence for exporting
    lineProc->lineangle = ui->spinBox_LineAngleRatio->value();  //line angle ratio for three pass search
    lineProc->cutoffB= ui->spinBox_PeakMinThreshold->value();  //minimum value for consideration as a peak  BLUE
    lineProc->cutoffR=  ui->spinBox_PeakMinThreshold->value();  //minimum value for consideration as a peak  RED
    lineProc->minThresh = ui->spinBox_MinCutoff->value();  //Lower threshold to ignore the low level noise
    lineProc->testLocation = ui->spinBox_TestParamLoc->value(); //location in the video where we will show a test of the parameters

}

void DialogProcessVideo::on_pushButton_Cam0_bigPicture_clicked()
{
    bigPic=new bigPicture(img0, this,0);
    connect(this, SIGNAL(updateBigPic(int,QImage)), bigPic, SLOT(updateBigPicture(int,QImage)));
    bigPic->show();
    bigPic->raise();
}

void DialogProcessVideo::on_pushButton_Cam1_bigPicture_clicked()
{
    bigPic=new bigPicture(img1, this,1);
    connect(this, SIGNAL(updateBigPic(int,QImage)), bigPic, SLOT(updateBigPicture(int,QImage)));
    bigPic->show();
    bigPic->raise();

}

void DialogProcessVideo::on_pushButton_triang3D_clicked()
{
    if(runningLineProcessing) return;  //don't start if linProc is going. (not likely since button is disabled)

    if(!runningTriang3D) { //run requested
    //check if previous results already exist
    if(QFile(output3D_0).exists() || QFile(output3D_1).exists())
        if(QMessageBox::question(this,
                       "Output Files Exist",
                       "Overwrite existing files?",
                       QMessageBox::Yes|QMessageBox::No,
                       QMessageBox::No   //default is "no"
                       ) != QMessageBox::Yes)
            return;  // if "Yes" is selected then continue, otherwise break



    if( triang3D_0->init(projName,eventName,0,camRes) ==0  //make sure return code is zero
            &&  triang3D_1->init(projName,eventName,1,camRes) ==0) //make sure return code is zero
    {
        runningTriang3D=true;
        ui->pushButton_LineProcessing->setEnabled(false);
        ui->pushButton_TestParams->setEnabled(false);
        ui->pushButton_triang3D->setText("Terminate");
        emit doItTriang3D();
    }
    } else  //terminate requested
        if(QMessageBox::question(this,
                                 "Triangulate 3D",
                                 "Terminate the 3D tranformation processors?",
                                 QMessageBox::Yes|QMessageBox::No,
                                 QMessageBox::No   //default is "no"
                                 ) == QMessageBox::Yes) {  // if "Yes" terminate, otherwise ignore
            emit termTriang3D(); //send signal to threads requesting terminate
            triang3DProcessingDone(); //clean up our dialog buttons
        }


}
void DialogProcessVideo::lineProcessingDone()
{
    //NOTE!! for threads completing: currently I don't check for both to finish.  The first one done will cause this to update. Should not be a problem since both video files will take about the same time to process.
    ui->pushButton_LineProcessing->setText("Line Processing");
    runningLineProcessing=false;
    ui->pushButton_Playback->setEnabled(true);
    ui->pushButton_triang3D->setEnabled(true);
    ui->pushButton_TestParams->setEnabled(true);

}

void DialogProcessVideo::triang3DProcessingDone()
{
    //NOTE!! for threads completing: currently I don't check for both to finish.  The first one done will cause this to update. Should not be a problem since both video files will take about the same time to process.
    ui->pushButton_triang3D->setText("Triang3D");
    runningTriang3D=false;
    ui->pushButton_LineProcessing->setEnabled(true);
    ui->pushButton_TestParams->setEnabled(true);

}

void DialogProcessVideo::on_pushButton_FE_export_clicked()
{
    int initResult = fe->init(ui->spinBox_NumIncrements->value());
    if(initResult == 0) {
        emit doItFEexport();
        ui->pushButton_FE_export->setEnabled(false);
    }
}

void DialogProcessVideo::FE_export_done()  //this slot gets signalled when FE Export Completes
{
    ui->pushButton_FE_export->setEnabled(true);
}
